#include "../config.h"
#include <glib.h>
#include "inotify-missing.h"
#include "inotify-path.h"

#define SCAN_MISSING_TIME 4000 /* 1/4 Hz */

static gboolean     im_debug_enabled = FALSE;
#define IM_W if (im_debug_enabled) g_warning

/* We put ih_sub_t's that are missing on this list */
static GList *missing_sub_list = NULL;
static gboolean im_scan_missing( gpointer user_data );
static gboolean scan_missing_running = FALSE;
static void ( *missing_cb )( ih_sub_t *sub ) = NULL;

G_LOCK_EXTERN( inotify_lock );

/* inotify_lock must be held before calling */
void im_startup( void ( *callback )( ih_sub_t *sub ) ) {
  static gboolean initialized = FALSE;
  if( !initialized ) {
    initialized = TRUE;
    missing_cb = callback;
  }
}

/* inotify_lock must be held before calling */
void im_add( ih_sub_t *sub ) {
  if( g_list_find( missing_sub_list, sub ) ) {
    IM_W( "asked to add %s to missing list but it's already on the list!\n", sub->pathname );
    return;
  }
  IM_W( "adding %s to missing list\n", sub->dirname );
  missing_sub_list = g_list_prepend( missing_sub_list, sub );
  /* If the timeout is turned off, we turn it back on */
  if( !scan_missing_running ) {
    scan_missing_running = TRUE;
    g_timeout_add( SCAN_MISSING_TIME, im_scan_missing, NULL );
  }
}

/* inotify_lock must be held before calling */
void im_rm( ih_sub_t *sub ) {
  GList *link;
  link = g_list_find( missing_sub_list, sub );
  if( !link ) {
    IM_W( "asked to remove %s from missing list but it isn't on the list!\n", sub->pathname );
    return;
  }
  IM_W( "removing %s from missing list\n", sub->dirname );
  missing_sub_list = g_list_remove_link( missing_sub_list, link );
  g_list_free_1( link );
}

/* Scans the list of missing subscriptions checking if they
   are available yet.
*/
static gboolean im_scan_missing( gpointer user_data ) {
  GList *nolonger_missing = NULL;
  GList *l;
  G_LOCK( inotify_lock );
  IM_W( "scanning missing list with %d items\n", g_list_length( missing_sub_list ) );
  for( l = missing_sub_list; l; l = l->next ) {
    ih_sub_t *sub = l->data;
    gboolean not_m = FALSE;
    IM_W( "checking %p\n", sub );
    g_assert( sub );
    g_assert( sub->dirname );
    not_m = ip_start_watching( sub );
    if( not_m ) {
      missing_cb( sub );
      IM_W( "removed %s from missing list\n", sub->dirname );
      /* We have to build a list of list nodes to remove from the
        missing_sub_list. We do the removal outside of this loop.
      */
      nolonger_missing = g_list_prepend( nolonger_missing, l );
    }
  }
  for( l = nolonger_missing; l ; l = l->next ) {
    GList *llink = l->data;
    missing_sub_list = g_list_remove_link( missing_sub_list, llink );
    g_list_free_1( llink );
  }
  g_list_free( nolonger_missing );
  /* If the missing list is now empty, we disable the timeout */
  if( missing_sub_list == NULL ) {
    scan_missing_running = FALSE;
    G_UNLOCK( inotify_lock );
    return FALSE;
  } else {
    G_UNLOCK( inotify_lock );
    return TRUE;
  }
}


/* inotify_lock must be held */
void
im_diag_dump( GIOChannel *ioc ) {
  GList *l;
  g_io_channel_write_chars( ioc, "missing list:\n", -1, NULL, NULL );
  for( l = missing_sub_list; l; l = l->next ) {
    ih_sub_t *sub = l->data;
    g_io_channel_write_chars( ioc, sub->pathname, -1, NULL, NULL );
    g_io_channel_write_chars( ioc, "\n", -1, NULL, NULL );
  }
}
